#include "./mdb_task.h"
#include "../driver/mdb_uart.h"
#include "../driver/cmd_uart.h"
#include "../driver/log_uart.h"

namespace task {

MdbTask mdbTask;

using namespace conn;
using namespace driver;
using namespace utils;

constexpr uint8_t RESET = 0x10;
constexpr uint8_t SETUP = 0x11;
constexpr uint8_t POLL = 0x12;
constexpr uint8_t VEND = 0x13;
constexpr uint8_t READER = 0x14;
constexpr uint8_t REVALUE = 0x15;
constexpr uint8_t EXT = 0x17;

struct MdbInfo {
    uint8_t level = 0;
    

    void set_price(uint16_t price) {
        m_price = price;
        m_pay_flag = true;
    }

    void clr_pay() {
        m_pay_flag = false;
    }

private:
    uint16_t m_price = 0;
    bool m_pay_flag = false;
public:
    void request() {
        DEBUG();
        if (!m_pay_flag) {
            return;
        }
        m_pay_flag = false;

        constexpr uint8_t SUB = 0x00;
        constexpr uint16_t YY = 0xFFFF;
        constexpr uint8_t ARG0 = 0x00;
        MdbResp resp;
        Error err = mdbUart.request(resp, VEND, SUB, m_price, YY);
        if (err) {
            logUart.printf("REQUEST err:%d\r\n", err.err_code);
            return;
        }
    }


};

static MdbInfo mdbInfo;

static void vend_request(const Message &msg) {
    DEBUG();

    uint8_t arg0;
    uint16_t price;
    Error err = msg.parse_req(arg0, price);
    if (err) {
        return;
    }
    mdbInfo.set_price(price);
    logUart.printf("request:%d\r\n", price);
    if (mdbInfo.level == 3) {
        mdbInfo.request();
    }

}   

static void vend_cancel(const Message &msg) {
    DEBUG();
    mdbInfo.clr_pay();
    constexpr uint8_t SUB = 0x01;
    MdbResp resp;
    mdbUart.request(resp, VEND, SUB);
}

static void vend_success(const Message &msg) {
    DEBUG();

    constexpr uint8_t SUB = 0x02;
    constexpr uint16_t YY = 0xFFFF;
    MdbResp resp;
    mdbUart.request(resp, VEND, SUB, YY);
}

static void vend_fail(const Message &msg) {
    DEBUG();

    constexpr uint8_t SUB = 0x03;
    MdbResp resp;
    mdbUart.request(resp, VEND, SUB);
}

static void session_complete(const Message &msg) {
    DEBUG();

    constexpr uint8_t SUB = 0x04;
    MdbResp resp;
    mdbUart.request(resp, VEND, SUB);
}

static void exec_pos(const Message &msg) {
    DEBUG();

    uint8_t arg0 = msg.u8arg0();

    logUart.printf("vend args=%d\r\n", arg0);

    switch (arg0) {
        case 0x00: vend_request(msg); break;
        case 0x01: vend_cancel(msg); break;
        case 0x02: vend_success(msg); break;
        case 0x03: vend_fail(msg); break;
        case 0x04: session_complete(msg); break;
    }
}

static void dispatch(const Message &msg) {
    DEBUG();
    msg.print_hex();
    uint8_t action = msg.action();

    switch (action) {
        case proto::ACTION_POS: exec_pos(msg); break;
    }
}

static void setup();
static void set_price();
static void ext0();
static void reader_enable();
static void poll_to_ack();
static void reset();
static void poll_once();

static void on_just_reset() {
    DEBUG();
    poll_once();
    setup();
    set_price();
    ext0();
    reader_enable();
    poll_to_ack();
}

static void on_session_cancel() {
    DEBUG();
    
    constexpr uint8_t Y1 = 0x04;
    MdbResp resp;
    Error err = mdbUart.request(resp, VEND, Y1);
    if (err) {
        logUart.printf("err:%d\r\n", err);
        return;
    }   
}

static void on_session_end() {
    DEBUG();
}

static void on_session_begin() {
    DEBUG();
    if (mdbInfo.level != 3) {
        mdbInfo.request();
    }
    
}

static void poll() {
    MdbResp resp;
    Error err = mdbUart.request(resp, POLL);
    if (err) {
        return;
    }
    if (resp.is_ack()) {
        return;
    }
    
    uint8_t head;
    err = resp.parse(head);
    if (err) {
        return;
    }
    logUart.printf("poll:%d\r\n", head);

    switch (head) {
        case 0x05: cmdUart.write(0x05); break;
        case 0x06: cmdUart.write(0x06); break;
        case 0x00: on_just_reset(); break;
        case 0x04: on_session_cancel(); break;
        case 0x07: on_session_end(); break;
        case 0x03: on_session_begin(); break;
    }
}

static void init() {
    DEBUG();
    poll_to_ack();
    reset();
    poll_once();
    setup();
    set_price();
    ext0();
    reader_enable();
    poll_to_ack();
}

void MdbTask::run() {
    init();
    logUart.printf("enter mdb event loop\r\n");
    Message msg;
    for (;;) {
        if (osOK == m_mq.poll(msg, 35)) {
            dispatch(msg);
        }
        poll();
    }
}

static void poll_to_ack() {
    DEBUG();

    MdbResp resp;
    for (;;) {
        Error err = mdbUart.request(resp, POLL);
        if (err.is_ok() && resp.is_ack()) {
            return;
        }
        logUart.printf("poll ack err:%d\r\n", err.err_code);
        osDelay(3000);
    }
}

static void poll_once() {
    DEBUG();

    MdbResp resp;
    mdbUart.request(resp, POLL);
}

static void reset() {
    DEBUG();

    MdbResp resp;
    for (;;) {
        Error err = mdbUart.request(resp, RESET);
        if (err.is_ok()) {
            return;
        }
        logUart.printf("reset err:%d\r\n", err.err_code);
        osDelay(500);
    }
}

static void setup() {
    DEBUG();

    MdbResp resp;
    constexpr uint8_t Y1 = 0x00;
    constexpr uint8_t Y2 = 1;
    constexpr uint8_t Y3 = 0;
    constexpr uint8_t Y4 = 0;
    constexpr uint8_t Y5 = 1;

    for (;;) {
        Error err = mdbUart.request(resp, SETUP, Y1, Y2, Y3, Y4, Y5);
        if (err.is_ok()) {
            break;
        }
        osDelay(50);
    }

    uint8_t z1;
    uint8_t level;
    uint16_t country_code;
    uint8_t scaling;
    uint8_t decimal;
    uint8_t time;
    uint8_t other;

    Error err = resp.parse(z1,
        level,
        country_code,
        scaling,
        decimal,
        time,
        other
    );

    if (err) {
        logUart.printf("setup parse err\r\n");
        return;
    }

    logUart.printf("z1=%d level=%d country_code=%04x %d %d %d %d\r\n", 
                z1, level, country_code, scaling, decimal, time, other
    );
    mdbInfo.level = level;
}

static void set_price() {
    DEBUG();

    MdbResp resp;
    constexpr uint8_t Y1 = 0x01;
    constexpr uint16_t Y2_3 = 0xFFFF;
    constexpr uint16_t Y4_5 = 0x00;

    for (;;) {
        Error err = mdbUart.request(resp, SETUP, Y1, Y2_3, Y4_5);
        if (err.is_ok()) {

            return;
        }
        osDelay(50);
    }
}

static void ext0() {
    DEBUG();

    MdbResp resp;
    constexpr uint8_t Y1 = 0x00;
    constexpr uint8_t MC = 'C';

    constexpr uint32_t S = 0x45454545;
    constexpr uint32_t M = 0x56565656;
    constexpr uint16_t V = 0x4565;

    for (;;) {
        Error err = mdbUart.request(resp, EXT, Y1, MC, MC, MC,
            S, S, S,
            M, M, M,
            V
        );
        if (err.is_ok()) {
            break;
        }
        osDelay(50);
    }

    if (mdbInfo.level != 3) {
        return;
    }
    constexpr uint8_t Y4 = 0x04;
    constexpr uint32_t Y2_5 = 1 << 5;
    for (;;) {
        Error err = mdbUart.request(resp, EXT, Y4, Y2_5);
        if (err.is_ok() && resp.is_ack()) {
            break;
        }
        osDelay(50);
    }

    logUart.printf("ext finish\r\n");
}

static void reader_enable() {
    DEBUG();

    MdbResp resp;
    constexpr uint8_t Y1 = 0x01;

    for (;;) {
        Error err = mdbUart.request(resp, READER, Y1);
        if (err.is_ok()) {
            return;
        }
        osDelay(50);
    }
}




}
